package vip.liux.backend.infrastructure.jpa.coupon;

import jakarta.persistence.criteria.Predicate;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.transaction.annotation.Transactional;
import vip.liux.contracts.models.coupon.UserCoupon;
import vip.liux.contracts.repositories.coupon.UserCouponRepository;
import vip.liux.contracts.shared.enums.coupon.UsedTag;
import vip.liux.contracts.shared.value.ReceivedRecord;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * 用户优惠券JPA仓库接口，继承自JpaRepository和UserCouponRepository。
 * 提供对UserCoupon实体的数据库操作及自定义查询方法。
 */
public interface UserCouponJpaRepository extends JpaRepository<UserCoupon, Long>, UserCouponRepository, JpaSpecificationExecutor<UserCoupon> {

    /**
     * 根据一组优惠券编号，统计每个优惠券的领取数量和领取人数。
     *
     * @param couponNos 优惠券编号集合
     * @return 优惠券领取记录列表
     */
    @Query("select uc.coupon.couponNo as couponNo, count(uc.id) as receivedCount, count(distinct uc.userId) as receivedCountPerson " +
            "from UserCoupon uc right join uc.coupon u " +
            "where u.couponNo in :couponNos " +
            "group by u.couponNo")
    @Override
    List<ReceivedRecord> findCountUserIdByCouponNoIn(Set<String> couponNos);

    /**
     * 查询指定用户领取指定优惠券的数量。
     *
     * @param userId   用户ID
     * @param couponNo 优惠券编号
     * @return 领取数量
     */
    @Query("select count(uc.id) from UserCoupon uc where uc.userId = :userId and uc.coupon.couponNo = :couponNo")
    @Override
    long findCountByUserIdAndCoupon(Long userId, String couponNo);

    /**
     * 查询指定优惠券被领取的总数量。
     *
     * @param couponNo 优惠券编号
     * @return 领取数量
     */
    @Query("select count(uc.id) from UserCoupon uc where uc.coupon.couponNo = :couponNo")
    @Override
    long findCountByCoupon(String couponNo);

    /**
     * 批量更新指定用户的已过期优惠券状态。
     *
     * @param userId  用户ID
     * @param usedTag 新的使用状态
     * @param now     当前时间
     * @return 更新的记录数
     */
    @Modifying
    @Query("update UserCoupon uc set uc.usedTag = :usedTag where uc.userId = :userId and uc.usedTag = :usedTag and uc.coupon.useEnd < :now")
    @Override
    @Transactional
    int updateExpiredCoupons(Long userId, UsedTag usedTag, LocalDateTime now);

    /**
     * 根据ID更新指定优惠券的过期状态。
     *
     * @param id      用户优惠券ID
     * @param usedTag 新的使用状态
     * @param now     当前时间
     * @return 更新的记录数
     */
    @Modifying
    @Query("update UserCoupon uc set uc.usedTag = :usedTag where uc.id in :id and uc.usedTag = :usedTag and uc.coupon.useEnd < :now")
    @Override
    @Transactional
    int updateExpiredCouponsById(List<Long> id, UsedTag usedTag, LocalDateTime now);

    /**
     * 分页查询用户优惠券。
     *
     * @param couponNo     优惠券编号
     * @param couponName   优惠券名称
     * @param userId       用户ID
     * @param usedTag      使用状态
     * @param receiveStart 领取开始时间
     * @param receiveEnd   领取结束时间
     * @param page         分页请求参数
     * @return 用户优惠券分页结果
     */
    @Override
    default Page<UserCoupon> getUserCoupons(
            String couponNo, String couponName, Long userId,
            UsedTag usedTag, LocalDateTime receiveStart, LocalDateTime receiveEnd, Pageable page) {
        return findAll((root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (StringUtils.isNotBlank(couponNo)) {
                predicates.add(criteriaBuilder.equal(root.get("coupon").get("couponNo"), couponNo));
            }
            if (StringUtils.isNotBlank(couponName)) {
                predicates.add(criteriaBuilder.like(root.get("coupon").get("couponName"), "%" + couponName + "%"));
            }
            if (userId != null) {
                predicates.add(criteriaBuilder.equal(root.get("userId"), userId));
            }
            if (usedTag != null) {
                predicates.add(criteriaBuilder.equal(root.get("usedTag"), usedTag));
            }
            if (receiveStart != null) {
                predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("receiveTime"), receiveStart));
            }
            if (receiveEnd != null) {
                predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get("receiveTime"), receiveEnd));
            }
            return query.where(predicates.toArray(new Predicate[0])).getRestriction();
        }, page);
    }
}